package service

import (
	"encoding/json"
	"fmt"
	"github.com/wonderivan/logger"
	"io/ioutil"
	"net/http"
	"time"
	"ucenter/app"
	"ucenter/app/model"
	"ucenter/app/validator"
)

type OauthError struct {
	Msg string
}

func (e *OauthError) Error() string {
	return e.Msg
}

var (
	thirdLoginInfoService *ThirdLoginInfoService
)

func GetThirdLoginInfoService() *ThirdLoginInfoService {
	if thirdLoginInfoService == nil {
		thirdLoginInfoService = &ThirdLoginInfoService{
			oauthClient: &http.Client{
				Transport: &http.Transport{
					MaxIdleConns:    app.Conf.OauthConf.MaxIdleConn,
					MaxConnsPerHost: app.Conf.OauthConf.MaxConnPerHost,
					IdleConnTimeout: time.Duration(app.Conf.OauthConf.IdleConnTimeout),
				},
				Timeout: time.Duration(app.Conf.OauthConf.Timeout),
			}}
	}
	return thirdLoginInfoService
}

/**
第三方校验登录
*/
type ThirdLoginInfoService struct {
	oauthClient *http.Client
}

/**
根据id_token拉取三方登录信息
*/
func (tl *ThirdLoginInfoService) FetchUserInfoByToken(loginType validator.LoginType, idToken string) (userInfo *model.ThirdUserInfo, err error) {
	if loginType == validator.GOOGLE {
		userInfo, err = tl.GoogleInfo(idToken)
	} else if loginType == validator.FACEBOOK {
		userInfo, err = tl.FacebookInfo(idToken)
	}
	return
}

//GoogleInfo get google sign-in user
//https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123
//https://developers.google.com/identity/sign-in/ios/backend-auth
func (tl *ThirdLoginInfoService) GoogleInfo(idToken string) (userInfo *model.ThirdUserInfo, err error) {
	var (
		resp     *http.Response
		respBody map[string]string
	)
	resp, err = tl.oauthClient.Get(fmt.Sprintf("https://oauth2.googleapis.com/tokeninfo?id_token=%s", idToken))
	if err != nil {
		logger.Error("Login_GOOGLE_FAIL | Err: %+v", err)
		err = &OauthError{"Login_GOOGLE_FAIL"}
		return
	}
	switch resp.StatusCode {
	case http.StatusBadRequest, http.StatusForbidden, http.StatusUnauthorized:
		err = json.NewDecoder(resp.Body).Decode(&respBody)
		if err != nil {
			logger.Error("Login_GOOGLE_FAIL_DECODE | Err: %+v", err)
			err = &OauthError{"Login_GOOGLE_FAIL_DECODE"}
			return
		}
		logger.Error("Login_FB_FAIL | StatusCode:%d | Resp:%+v", resp.StatusCode, respBody)
		if msg, ok := respBody["error"]; ok {
			err = &OauthError{msg}
		} else {
			err = &OauthError{"Login_GOOGLE_FAIL_Unknown"}
		}
		return

	case http.StatusOK:
		userInfo = &model.ThirdUserInfo{}
		err = json.NewDecoder(resp.Body).Decode(&respBody)
		if err != nil {
			logger.Error("Login_GOOGLE_FAIL_DECODE | Err: %+v", err)
			err = &OauthError{"Login_GOOGLE_FAIL_DECODE"}
			return
		}
		userInfo.Id, _ = respBody["sub"]
		userInfo.Name, _ = respBody["name"]
		userInfo.Avatar, _ = respBody["picture"]
	default:
		logger.Error("Login_GOOGLE_FAIL_Unknown | StatusCode:%d ", resp.StatusCode)
		err = &OauthError{"Login_GOOGLE_FAIL_Unknown"}
	}

	return
}

//FacebookInfo get facebook user basic info
//https://graph.facebook.com/{user-id}?access_token={Token}
//https://graph.facebook.com/me?fields=email,name,picture&access_token={token}
func (tl *ThirdLoginInfoService) FacebookInfo(idToken string) (userInfo *model.ThirdUserInfo, err error) {
	var (
		resp   *http.Response
		fbErr  *model.FacebookError
		fbUser *model.FacebookUserInfo
	)
	resp, err = tl.oauthClient.Get(fmt.Sprintf("https://graph.facebook.com/me?fields=email,name,picture&access_token=%s", idToken))
	println(fmt.Sprintf("https://graph.facebook.com/me?fields=email,name,picture&access_token=%s", idToken))
	if err != nil {
		return
	}
	switch resp.StatusCode {
	case http.StatusBadRequest, http.StatusForbidden, http.StatusUnauthorized:
		fbErr = &model.FacebookError{}
		err = json.NewDecoder(resp.Body).Decode(fbErr)
		if err != nil {
			logger.Error("Login_FB_FAIL_DECODE | Err: %+v", err)
			err = &OauthError{"Login_FB_FAIL_DECODE"}
			return
		}
		logger.Error("Login_FB_FAIL | StatusCode:%d | Resp:%+v", resp.StatusCode, fbErr)
		err = &OauthError{fbErr.Error.Message}
	case http.StatusOK:
		fbUser = &model.FacebookUserInfo{}
		err = json.NewDecoder(resp.Body).Decode(fbUser)
		if err != nil {
			content, _ := ioutil.ReadAll(resp.Body)
			logger.Error("Login_FB_FAIL_DECODE | Content: %s", content)
			err = &OauthError{"Login_FB_FAIL_DECODE"}
			return
		}
		userInfo = &model.ThirdUserInfo{
			Id:     fbUser.Id,
			Name:   fbUser.Name,
			Avatar: fbUser.Picture.Data.URL,
		}
	default:
		logger.Error("Login_FB_FAIL_Unknown | StatusCode:%d ", resp.StatusCode)
		err = &OauthError{"Login_FB_FAIL_Unknown"}
	}
	return
}
